package actor

import (
	"errors"
	"lnzw/engine/log"
	"sync"
	"sync/atomic"
	"time"

	"go.uber.org/zap"
)

// ActorModel
type ActorModel func() IActor

// 当前活跃的actor (如果要实现跨服务器访问actor，
// 咱们可以将actor信息放到redis里面，然后通过rpc通讯的方式，传递消息哟)
var actors = sync.Map{}

// 注册的生成指定别名的生成actror的函数
var actorModels = map[string]ActorModel{}

// 初始化actorId生成器
var id int64 = time.Now().UnixNano()

// 生成actorId
func GenId() int64 {
	actorId := atomic.AddInt64(&id, 1)
	return actorId
}

// 注册actor生成函数
func RegisterActor(catalog string, actorModel ActorModel) {
	actorModels[catalog] = actorModel
}

// 启动actor
func StartActor(catalog string, actorId string) IActor {
	actor := actorModels[catalog]()
	actor.SetActorId(actorId)
	actor.SetCatalog(catalog)
	_, loaded := actors.LoadOrStore(actorId, actor)
	if !loaded {
		actor.Init()
		actor.Start()
		actor.RegisterHandle(actor.Handle)
	}
	return actor
}

// 获得actor
func GetActor(actorID string) IActor {
	actor, loaded := actors.Load(actorID)
	if loaded {
		return actor.(IActor)
	}
	return nil
}

// 关闭actor
func StopActor(actorID string) {
	actor, loaded := actors.LoadAndDelete(actorID)
	if !loaded {
		log.Error("actor not exist")
		return
	}
	actor.(IActor).Stop()
}

// 异步给指定的actor发送消息
func Send(sourceId string, actorID string, data []byte) error {
	actor := GetActor(actorID)
	if actor == nil {
		log.Error("actor not exist")
		return errors.New("actor not exist")
	}
	actor.Send(Msg{
		Data:     data,
		SourceId: sourceId,
	})
	return nil
}

// 同步给指定的actor发送消息
func SyncSend(sourceId string, actorID string, data []byte) (Msg, error) {
	actor := GetActor(actorID)
	if actor == nil {
		log.Error("actor not exist", zap.String("actorID", actorID))
		return Msg{}, errors.New("actor not exist")
	}
	ch := make(chan Msg)
	defer close(ch)
	actor.SyncSend(Msg{
		Data:     data,
		SourceId: sourceId,
	}, ch)
	select {
	case msg := <-ch:
		return msg, nil
	case <-time.After(5 * time.Second):
		log.Warn("actor.SyncSend reply timeout")
		return Msg{}, errors.New("actor.SyncSend reply timeout")
	}
}
